home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2000 / MacHack 2000.toast / pc / The Hacks / PostScript OSA / DropShell3 / DropShell3.cp < prev    next >
Encoding:
Text File  |  2000-06-23  |  28.4 KB  |  1,000 lines

  1. /******************************************************************************
  2. **
  3. **  Project Name:    DropShell
  4. **     File Name:    DropShell.c
  5. **
  6. **   Description:    Main application code for the QuickShell
  7. **
  8. *******************************************************************************
  9. **                       A U T H O R   I D E N T I T Y
  10. *******************************************************************************
  11. **
  12. **    Initials    Name
  13. **    --------    -----------------------------------------------
  14. **    LDR            Leonard Rosenthol
  15. **    MTC            Marshall Clow
  16. **    SCS            Stephan Somogyi
  17. **
  18. *******************************************************************************
  19. **                      R E V I S I O N   H I S T O R Y
  20. *******************************************************************************
  21. **
  22. **      Date        Author    Description
  23. **    ---------    ------    ---------------------------------------------
  24. **    22 Aug    96    MTC        Merged the AppleEvent stuff into this file.
  25. **    22 Jul    96    MTC        Now in C++!
  26. **    23 Jun    94    LDR        Implemented support for disk insertion events
  27. **    02 Feb    94    LDR        Updated for Final SDK & CodeWarrior a2
  28. **                        Removed the ResumeProc as per new Apple recommendations
  29. **    11 Dec 93    SCS        Universal Headers/UPPs (Phoenix 68k/PPC & PPCC)
  30. **                        Skipped System 6 compatible rev of DropShell source
  31. **    09 Dec 91    LDR        Added support for new "Select File…" menu item
  32. **                        Quit now sends AEVT to self to be politically correct
  33. **                        Added support for the new gSplashScreen
  34. **    24 Nov 91    LDR        Added support for the Apple Menu (duh!)
  35. **    29 Oct 91    SCS        Changes for THINK C 5
  36. **    28 Oct 91    LDR        Officially renamed DropShell (from QuickShell)
  37. **                        Added a bunch of comments for clarification
  38. **    06 Oct 91    MTC        Converted to MPW C
  39. **    09 Apr 91    LDR        Added to Projector
  40. **
  41. ******************************************************************************/
  42.  
  43. #include <Types.h>
  44. #include <Dialogs.h>
  45. #include <DiskInit.h>
  46. #include <Errors.h>
  47. #include <Files.h>
  48. #include <Fonts.h>
  49. #include <Memory.h>
  50. #include <Menus.h>
  51. #include <StandardFile.h>
  52. #include <TextEdit.h>
  53. #include <Windows.h>
  54.  
  55. #ifndef    __DROPSHELL3__
  56. #include "DropShell3.h"
  57. #endif
  58.  
  59. #ifndef    _FILEITERATORS_
  60. #include "FileIterators.h"
  61. #endif
  62.  
  63. #include "DS3Resources.h"
  64.  
  65.  
  66. struct DSConfigRec {
  67.     UInt32        mmCount;        // How many times should we call MoreMasters?
  68.     UInt32        tpCount;        // How big a thread pool should we allocate?
  69. };
  70. typedef struct DSConfigRec DSConfigRec, *DSConfigPtr, **DSConfigHdl;
  71.  
  72.  
  73. pascal void        DoHighLevelEvent(EventRecord *event);
  74. static void SendQuitToSelf (void);
  75. static void ErrorAlert ( short alertID, short stringListID, short stringIndexID, short errorID );
  76.  
  77.  
  78. /*
  79.     Nothing fancy.  Just setting up the basic menus.
  80. */
  81. #pragma segment Initialize
  82. void CDropShell::SetUpMenus (void) {
  83.  
  84. //    Add the Apple Menu
  85.     fAppleMenu = GetMenu ( kAppleNum );
  86.     // AssertStr ( ValidHandle ((Handle) fAppleMenu ), "\pCan't load Apple Menu" );
  87.     AppendResMenu ( fAppleMenu, 'DRVR' );
  88.     InsertMenu ( fAppleMenu, 0 );
  89.  
  90. //    Add the file menu
  91.     fFileMenu = GetMenu ( kFileNum );
  92.     // AssertStr ( ValidHandle ((Handle) fAppleMenu ), "\pCan't load File Menu" );
  93.     InsertMenu ( fFileMenu, 0 );
  94.  
  95. //    And the edit menu
  96.     fEditMenu = GetMenu ( kEditNum );
  97.     // AssertStr ( ValidHandle ((Handle) fAppleMenu ), "\pCan't load Edit Menu" );
  98.     if ( fEditMenu != nil )
  99.         InsertMenu ( fEditMenu, 0 );
  100.     }
  101.  
  102. /*
  103.     This routine is called during startup to display a splash screen.
  104.     
  105.     This was recommend by the Blue Team HI person, John Sullivan, who
  106.     feels that all apps should display something so that users can easily
  107.     tell what is running, and be able to switch by clicking.  Thanks John!
  108. */
  109. #pragma segment Initialize
  110. static void CenterWindow ( WindowRef theWindow ) {
  111.     SInt16      menuBarHeight    = LMGetMBarHeight ();
  112.     GDHandle    gd                = GetMainDevice ();
  113.     Rect        wRect            = GetWindowPort ( theWindow )->portRect;
  114.     SInt16      r, c, b;
  115.  
  116.     b = (*gd)->gdRect.bottom;
  117.     r = b - (*gd)->gdRect.top;
  118.     c = wRect.bottom - wRect.top;
  119.  
  120. //    Original code was:     if (c / r > 0.9)
  121. //    I changed it to remove floating point dependencies
  122. //    Note that c is never going to be greater than 2000 here, so *10 is harmless.
  123.     if (( 10 * c ) / r > 9 )
  124.         c = ( b - wRect.bottom - wRect.top ) / 2 + menuBarHeight;
  125.     else
  126.         c = (b - wRect.bottom - wRect.top ) / 3 + menuBarHeight;
  127.  
  128.     r = ((*gd)->gdRect.right - wRect.right - wRect.left ) / 2;
  129.  
  130.     MoveWindow ( theWindow, r, c, false);
  131.     }
  132.  
  133.  
  134. #pragma segment Initialize
  135. void CDropShell::InstallSplashScreen ( void ) {
  136.     #define windowPicID    128
  137.  
  138.     PicHandle    picH;
  139.  
  140.     if ( fSplashScreen == nil ) {  // show the splash screen window
  141.         picH = GetPicture ( windowPicID );
  142.         if ( picH != NULL ) {
  143.             Rect r = (*picH)->picFrame;
  144.             fSplashScreen = GetNewWindow ( windowPicID, NULL, (WindowPtr) -1L );
  145.             if ( fSplashScreen != nil ) {
  146.             //    Make the window the same size as the picture
  147.                 SizeWindow ( fSplashScreen, r.right - r.left, r.bottom - r.top, true );
  148.                 CenterWindow ( fSplashScreen );
  149.                 SetWindowPic ( fSplashScreen, picH );
  150.                 }
  151.             }
  152.         }
  153.     }
  154.  
  155.  
  156. /*    --------------- Standard Event Handling routines ---------------------- */
  157. #pragma segment Main
  158. void CDropShell::ShowAbout () {
  159.     (void) Alert ( 128, NULL );
  160.     }
  161.  
  162.  
  163. #pragma segment Main
  164. void CDropShell::DoMenu ( long menuChoice ) {
  165.     short    menuID, itemID;
  166.     Str255    itemStr;
  167.  
  168.     menuID = HiWord ( menuChoice );
  169.     itemID = LoWord ( menuChoice );
  170.     
  171.     switch ( menuID ) {
  172.         case kAppleNum:
  173.             if ( itemID == 1 )
  174.                 ShowAbout ();    /*    Show the about box */
  175.             else {
  176.                 GetMenuItemText ( GetMenuHandle ( kAppleNum ), itemID, itemStr );
  177.                 OpenDeskAcc ( itemStr );
  178.                 }
  179.             break;
  180.             
  181.     //    Handle the first and the last items in the file menu
  182.     //    other ones are up to the actual app.
  183.         case kFileNum:
  184.             if ( itemID == 1 )
  185.                 SelectFile ();        // call file selection userProc
  186.             else if ( itemID == CountMItems ( fFileMenu ))
  187.                 SendQuitToSelf ();    // send self a 'quit' event
  188.             break;
  189.         
  190.         default:
  191.             break;
  192.             
  193.         }
  194.         
  195.     HiliteMenu ( 0 );        // turn off the menu
  196.     }
  197.  
  198.  
  199. #pragma segment Main
  200. void CDropShell::DoMouseDown ( EventRecord *curEvent ) {
  201.     WindowPtr    whichWindow;
  202.     short        whichPart;
  203.  
  204.     whichPart = FindWindow ( curEvent->where, &whichWindow );
  205.     switch ( whichPart ) {
  206.         case inMenuBar:
  207.             DoMenu ( MenuSelect ( curEvent->where ));
  208.             break;
  209.         
  210.         case inSysWindow:
  211.             SystemClick ( curEvent, whichWindow );
  212.             break;
  213.         
  214.         case inDrag:
  215.             {
  216.                 Rect    boundsRect = (*GetGrayRgn())->rgnBBox;
  217.                 DragWindow ( whichWindow, curEvent->where, &boundsRect );
  218.             }
  219.         default:
  220.             break;
  221.         }
  222.     }
  223.  
  224.  
  225. #pragma segment Main
  226. void CDropShell::DoKeyDown ( EventRecord *curEvent ) {
  227.     if ( curEvent->modifiers & cmdKey )
  228.         DoMenu ( MenuKey ((char) curEvent->message & charCodeMask ));
  229.     }
  230.  
  231.  
  232. #pragma segment Main
  233. void CDropShell::EventLoop( void )
  234. {
  235.     EventRecord    anEvent;
  236.                 
  237.     if ( WaitNextEvent ( everyEvent, &anEvent, 0, NULL )) {
  238.         switch ( anEvent.what ) {
  239.             case kHighLevelEvent:
  240.                 DoHighLevelEvent ( &anEvent );
  241.                 break;
  242.                 
  243.             case mouseDown:
  244.                 DoMouseDown ( &anEvent );
  245.                 break;
  246.                 
  247.             case keyDown:
  248.             case autoKey:
  249.                 DoKeyDown ( &anEvent );
  250.                 break;
  251.  
  252.             case diskEvt:
  253.                 if ( HiWord ( anEvent.message )) {
  254.                     Point diskInitPt = { 100, 100 };
  255.                     
  256.                     DILoad ();
  257.                     DIBadMount ( diskInitPt, anEvent.message );
  258.                     DIUnload ();
  259.                     }
  260.                 break;
  261.                 
  262.             default:
  263.                 break;
  264.             }
  265.         }
  266. }
  267.  
  268. #pragma segment Main
  269. void CDropShell::Run ( ) {
  270.     OSErr    err = noErr;
  271.     
  272. //    We don't run if there are no AppleEvents!
  273.     if ( !fHasAppleEvents )
  274.         ErrorAlert ( kAlertID, kErrStringID, kCantRunErr, 0 );
  275.     else {
  276.         
  277.     //    Install the required appleEvent handlers
  278.         if (( err = InitAEVTStuff ()) != noErr )
  279.             ErrorAlert ( kAlertID, kErrStringID, kInitAErr, err );
  280.         else {
  281.         //    We want to draw the menu bar ourselves instead of
  282.         //    letting SetupMenus call it, because SetupMenus might be
  283.         //    overridden, and we don't want a flash mtc 08-14-96
  284.             SetUpMenus ();
  285.             DrawMenuBar ();
  286.  
  287.         //    Install the splash screen. This only shows if the user 
  288.         //    double-clicked on the application.
  289.             InstallSplashScreen ();
  290.  
  291.             if (( err = InitGlobals ()) != noErr )
  292.                 ErrorAlert ( kAlertID, kErrStringID, kInitProgErr, err );
  293.             else {
  294.                 while ( !fDone ) {
  295.                     EventLoop();
  296.                 }
  297.  
  298.             //    Clean up whatever globals we need
  299.                 DeInitGlobals ();            
  300.                 }
  301.             }
  302.         }
  303.     }
  304.     
  305.  
  306. #ifdef MPW
  307. extern "C" { void _DataInit (); }
  308. #endif
  309.  
  310. CDropShell::CDropShell ( Boolean walkFolders ) : fWalkFolders ( walkFolders ) {
  311.     long aLong;
  312.  
  313. #ifdef MPW
  314.     UnloadSeg ((Ptr) _DataInit );
  315. #endif
  316.  
  317. //    Initialize the toolbox
  318.     InitGraf ( &qd.thePort );
  319.     InitFonts ();
  320.     InitWindows ();
  321.     InitMenus ();
  322.     TEInit ();
  323.     InitDialogs ( 0 );
  324.     InitCursor ();
  325.     FlushEvents ( everyEvent, 0 );
  326.     
  327. //    Initialize the globals for the app
  328.     fDone            = false;
  329.     fOApped            = false;    // probably not since users are supposed to DROP things!
  330.     fHasAppleEvents    = Gestalt ( gestaltAppleEventsAttr, &aLong ) == noErr;
  331.     fHasThreadMgr    = Gestalt ( gestaltThreadMgrAttr, &aLong ) == noErr && ( aLong & ( 1 << gestaltThreadMgrPresent )) != 0;
  332.     fSplashScreen    = NULL;
  333.     fAppleMenu        = NULL;
  334.     fFileMenu        = NULL;
  335.     fEditMenu        = NULL;
  336.     
  337. //    Load a resource for params
  338.     UInt32 mmCount = 2;
  339.     UInt32 tpCount = 0;
  340.     DSConfigHdl    conf = (DSConfigHdl) GetResource ( 'Conf', 128 );
  341.     if ( conf != nil ) {
  342.         mmCount = (*conf)->mmCount;
  343.         tpCount = (*conf)->tpCount;
  344.         ReleaseResource ((Handle) conf );
  345.         }
  346.  
  347.     MaxApplZone();
  348.     for ( UInt32 i = 0; i < mmCount; i++ )
  349.         MoreMasters ();
  350.     
  351. //    If we don't want threaded apps, then pretend we don't have it!
  352.     if ( fHasThreadMgr && tpCount == 0 )
  353.         fHasThreadMgr = false;
  354.  
  355. //    Allocate a thread pool
  356.     if ( fHasThreadMgr && tpCount > 0 ) {
  357.         }
  358.     }
  359.  
  360. CDropShell::~CDropShell() {}
  361.  
  362. #pragma mark ---AppleEvents---
  363.  
  364. OSErr            GotRequiredParams(AppleEvent *theAppleEvent);
  365.  
  366. //pascal OSErr    _HandleDocs ( AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon, Boolean opening );
  367.  
  368. pascal OSErr    HandleOAPP(AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon);
  369. pascal OSErr    HandleQuit(AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon);
  370. pascal OSErr    HandleODOC(AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon);
  371. pascal OSErr    HandlePDOC(AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon);
  372.  
  373. /*
  374.     This routine does all initialization for AEM, including the
  375.     creation and then population of the dispatch table.
  376. */
  377. #pragma segment Initialize
  378. OSErr CDropShell::InitAEVTStuff ()  {
  379.     OSErr err = noErr;
  380.  
  381.     if ( err == noErr )
  382.         err = AEInstallEventHandler ( kCoreEventClass, kAEOpenApplication, 
  383.                 NewAEEventHandlerProc ( HandleOAPP ), (long) this, false );
  384.  
  385.     if ( err == noErr )
  386.         err = AEInstallEventHandler ( kCoreEventClass, kAEOpenDocuments, 
  387.                 NewAEEventHandlerProc ( HandleODOC ), (long) this, false );
  388.  
  389.     if ( err == noErr )
  390.         err = AEInstallEventHandler ( kCoreEventClass, kAEPrintDocuments, 
  391.                 NewAEEventHandlerProc ( HandlePDOC ), (long) this, false );
  392.  
  393.     if ( err == noErr )
  394.         err = AEInstallEventHandler ( kCoreEventClass, kAEQuitApplication, 
  395.                 NewAEEventHandlerProc ( HandleQuit ), (long) this, false );
  396.  
  397.     // ReportError ( err );
  398.     return err;
  399.     }
  400.  
  401.  
  402.  
  403. /*
  404.     This routine will create a targetDesc for sending to self.
  405.  
  406.     We take IM VI's advice and use the typePSN form with 
  407.     kCurrentProcess as the targetPSN.
  408. */
  409. static OSErr GetTargetFromSelf ( AEAddressDesc *targetDesc ) {
  410.     OSErr        err = noErr;
  411.     ProcessSerialNumber    psn;
  412.  
  413.     psn.highLongOfPSN     = 0;
  414.     psn.lowLongOfPSN     = kCurrentProcess;
  415.  
  416.     err = AECreateDesc(typeProcessSerialNumber, (Ptr)&psn, sizeof(ProcessSerialNumber), targetDesc );
  417.     
  418.     // ReportError ( err );
  419.     return err;
  420.     }
  421.  
  422.  
  423. /*
  424.     This routine is the low level routine used by the SendODOCToSelf
  425.     routine.  It gets passed the list of files (in an AEDescList)
  426.     to be sent as the data for the 'odoc', builds up the event
  427.     and sends off the event.  
  428.  
  429.     It is broken out from SendODOCToSelf so that a SendODOCListToSelf could
  430.     easily be written and it could then call this routine - but that is left
  431.     as an exercise to the reader.
  432.     
  433.     Read the comments in the code for the order and details
  434. */
  435. static void _SendDocsToSelf ( AEDescList *fileList ) {
  436.     OSErr            err = noErr;
  437.     AEAddressDesc    theTarget;
  438.     AppleEvent        openDocAE, replyAE;
  439.  
  440. /*
  441.     First we create the target for the event.   We call another
  442.     utility routine for creating the target.
  443. */
  444.     err = GetTargetFromSelf ( &theTarget );
  445.     if ( err == noErr ) {
  446.         /* Next we create the Apple event that will later get sent. */
  447.         err = AECreateAppleEvent ( kCoreEventClass, kAEOpenDocuments,
  448.                 &theTarget, kAutoGenerateReturnID, kAnyTransactionID, &openDocAE );
  449.  
  450.         if ( err == noErr ) {
  451.             /* Now add the fileDescList to the openDocAE */
  452.             err = AEPutParamDesc ( &openDocAE, keyDirectObject, fileList );
  453.  
  454.             if ( err == noErr ) {
  455.             /*    and finally send the event */
  456.             /*    Since we are sending to ourselves, no need for reply. */
  457.                 err = AESend ( &openDocAE, &replyAE, kAENoReply + kAECanInteract,
  458.                             kAENormalPriority, 3600, NULL, NULL );
  459.             //    AssertStr ( err == noErr, "\pError Sending 'odoc' Apple Event" );
  460.  
  461.             /*    NOTE: Since we are not requesting a reply, we do not need to */
  462.             /*    need to dispose of the replyAE.  It is there simply as a */
  463.             /*    placeholder. */
  464.                 }
  465.  
  466.         /*    Dispose of the fileList descriptor
  467.             We do this instead of the caller since it needs to be done
  468.             before disposing the AEVT
  469.         */
  470.             err = AEDisposeDesc ( fileList );
  471.         //    AssertStr ( err == noErr, "\pError Disposing of File list" );
  472.  
  473.         /*    and of course dispose of the openDoc AEVT itself*/
  474.             err = AEDisposeDesc ( &openDocAE );
  475.         //    AssertStr ( err == noErr, "\pError Disposing of AppleEvent" );
  476.             }
  477.  
  478.     /*    and of course dispose of the Target */
  479.         err = AEDisposeDesc ( &theTarget );
  480.         // AssertStr ( err == noErr, "\pError Disposing of Target" );
  481.         }
  482.  
  483.     // AssertStr ( err == noErr, "\p_SendDocsToSelf" );
  484.     }
  485.  
  486. /*
  487.     This is the routine called by SelectFile to send a single odoc to ourselves.
  488.     
  489.     It calls the above low level routine to do the dirty work of sending the AEVT -
  490.     all we do here is build a AEDescList of the file to be opened.
  491. */
  492. void SendODOCToSelf ( FSSpecPtr theFileSpec ) {
  493.     OSErr        err = noErr;
  494.     AEDescList    fileList;
  495.     
  496. /*    Create the descList to hold the list of files */
  497.     err = AECreateList ( NULL, 0, false, &fileList );
  498.  
  499.     if ( err == noErr ) {
  500.     /*    Add the FSSpec to the list */
  501.         err = AEPutPtr ( &fileList, 0, typeFSS, theFileSpec, sizeof ( FSSpec ));
  502.     //    AssertStr ( err == noErr, "\pError Adding FSSpec to AEDesc" );
  503.  
  504.     /*    Now call the real gut level routine to do the dirty work*/
  505.         if ( err == noErr )
  506.             _SendDocsToSelf ( &fileList );
  507.  
  508.     /*    _SendDocsToSelf will dispose of fileList for me */
  509.         }
  510.  
  511.     // AssertStr ( err == noErr, "\pSendODOCToSelf" );
  512.     }
  513.  
  514.  
  515. static void SendQuitToSelf ( void ) {
  516.     OSErr            err = noErr;
  517.     AEAddressDesc    theTarget;
  518.     AppleEvent        quitAE, replyAE;
  519.  
  520. /*
  521.     First we create the target for the event.   We call another
  522.     utility routine for creating the target.
  523. */
  524.     err = GetTargetFromSelf ( &theTarget );
  525.     if ( err == noErr ) {
  526.         
  527.     /*    Next we create the Apple event that will later get sent. */
  528.         err = AECreateAppleEvent ( kCoreEventClass, kAEQuitApplication,
  529.                 &theTarget, kAutoGenerateReturnID, kAnyTransactionID, &quitAE );
  530.  
  531.         if ( err == noErr ) {
  532.             /*
  533.                 and finally send the event
  534.                 Since we are sending to ourselves, no need for reply.
  535.             */
  536.             err = AESend ( &quitAE, &replyAE, kAENoReply + kAECanInteract, kAENormalPriority, 3600, NULL, NULL );
  537.         //    AssertStr ( err == noErr, "\pError Sending 'quit' Apple Event" );
  538.  
  539.             /*
  540.                 NOTE: Since we are not requesting a reply, we do not need to
  541.                 need to dispose of the replyAE.  It is there simply as a 
  542.                 placeholder.
  543.             */
  544.  
  545.         /*    Dispose of the quit AEVT */
  546.             err = AEDisposeDesc ( &quitAE );
  547.     //        AssertStr ( err == noErr, "\pError disposing 'quit' Apple Event" );
  548.             }
  549.  
  550.     /*    Dispose of the target. */
  551.         err = AEDisposeDesc ( &theTarget );
  552.     //    AssertStr ( err == noErr, "\pError disposing target" );
  553.         }
  554.         
  555. //    AssertStr ( err == noErr, "\pSendQuitToSelf" );
  556.     }
  557.  
  558. /*    
  559.     This routine is a utility routine for checking that all required 
  560.     parameters in the Apple event have been used.
  561. */
  562. #pragma segment Main
  563. OSErr GotRequiredParams ( AppleEvent *theAppleEvent ) {
  564.     DescType    typeCode;
  565.     Size        actualSize;
  566.     OSErr        retErr, err;
  567.  
  568.     err = AEGetAttributePtr ( theAppleEvent, keyMissedKeywordAttr,
  569.                     typeWildCard, &typeCode, NULL, 0, &actualSize );
  570.     
  571.     if ( err == errAEDescNotFound )    // we got all the required params: all is ok
  572.         retErr = noErr;
  573.     else if ( err == noErr )
  574.         retErr = errAEEventNotHandled;
  575.     else 
  576.         retErr = err;
  577.     
  578.     // ReportError ( retErr );
  579.     return retErr;
  580.     }
  581.  
  582. /*    
  583.     This routine is the handler for the oapp (Open Application) event.
  584.     
  585.     It first checks the number of parameters to make sure we got them all 
  586.     (even though we don't want any) and then calls the OpenApp userProc in QSUserProcs.
  587.     Finally it checks to see if the caller wanted a reply & sends one, setting any error.
  588. */
  589. #pragma segment Main
  590. pascal OSErr HandleOAPP ( AppleEvent * /*theAppleEvent*/, AppleEvent *reply, long handlerRefcon ) {
  591.     CDropShell *theApp = (CDropShell *) handlerRefcon;
  592.     OSErr         err = noErr;
  593.     
  594.     theApp->OpenApp ();        // pass it on to the app specific routine
  595.  
  596.     if ( reply->dataHandle != NULL )    /*    a reply is sought */
  597.         err = AEPutParamPtr ( reply, 'errs', 'TEXT', "Opening", 7 );
  598.  
  599. //    Report an error here!
  600.     if ( err != noErr )    {
  601.         ErrorAlert ( kAlertID, kErrStringID, kAEVTErr, err );
  602.         }
  603.     
  604.     // ReportError ( err );
  605.     return err;
  606.     }
  607.  
  608.  
  609. /*    
  610.     This routine is the handler for the quit (Quit Application) event.
  611.     It calls the QuitApp method for the app.
  612.     Finally it checks to see if the caller wanted a reply & sends one, setting any error.
  613. */
  614.  
  615. #pragma segment Main
  616. pascal OSErr HandleQuit ( AppleEvent * /*theAppleEvent*/, AppleEvent *reply, long handlerRefcon ) {
  617.     CDropShell *theApp = (CDropShell *) handlerRefcon;
  618.     OSErr        err = noErr;
  619.     
  620.     theApp->QuitApp ();        // pass it on to the app specific routine
  621.     if ( reply->dataHandle != NULL )    /*    a reply is sought */
  622.         err = AEPutParamPtr ( reply, 'errs', 'TEXT', "Qutting", 7 );
  623.     
  624. //    Report an error here!
  625.     if ( err != noErr )    {
  626.         ErrorAlert ( kAlertID, kErrStringID, kAEVTErr, err );
  627.         }
  628.     
  629.     // ReportError ( err );
  630.     return err;
  631.     }
  632.  
  633.  
  634. /*    
  635.     This routine is the low level processing routine for both the 
  636.     odoc (Open Document) and pdoc (Print Document) events.
  637.     
  638.     This routine is the key one, since this is how we get the list of
  639.     files/folders/disks to process.  The first thing to do is the get the
  640.     list of files, and then make sure that's all the parameters (should be!).
  641.     We then send call the PreflightDocs routine (from DSUserProcs), process
  642.     each file in the list by calling OpenDoc (again in DSUserProcs), and finally
  643.     call PostflightDocs (you know where) and setting a return value.
  644. */
  645. #pragma segment Main
  646. static pascal OSErr _HandleDocs ( AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon, Boolean opening ) {
  647.     CDropShell *theApp = (CDropShell *) handlerRefcon;
  648.     OSErr        err = noErr;
  649.     OSErr        err2;
  650.     AEDescList    docList;
  651.     
  652. //    Get the list
  653. //    Note that (for some reason) we have to call AEGetParamDesc _before_ we call
  654. //    GotRequiredParams, or GotRequiredParams will return an error.
  655.     err = AEGetParamDesc ( theAppleEvent, keyDirectObject, typeAEList, &docList );
  656.     if ( err == noErr ) {
  657.         err = GotRequiredParams ( theAppleEvent );
  658.  
  659.     /*    How many items do we have?. */
  660.     /*    NOTE: Moved here in DS 2.0 due to requests for this info in preflighter */
  661.         if ( err == noErr ) {
  662.             long itemsInList;
  663.             err = AECountItems ( &docList, &itemsInList );
  664.             
  665.             if ( err == noErr ) {
  666.                 if ( !theApp->PreFlightDocs ( opening, (short) itemsInList ))
  667.                     err = errAEEventNotHandled;    // tells AEM that we didn't handle it!
  668.                 else {
  669.                     FSSpec        myFSS;
  670.                     long        index;
  671.                     Size        actualSize;
  672.                     AEKeyword    keywd;
  673.                     DescType    typeCode;
  674.  
  675.                 //    process each file in the list
  676.                     for ( index = 1; err == noErr && index <= itemsInList; index++ ) {
  677.                         if (( err = AEGetNthPtr ( &docList, index, typeFSS, &keywd, &typeCode,
  678.                                         (Ptr) &myFSS, sizeof ( myFSS ), &actualSize )) == noErr )
  679.                             err = theApp->OpenDoc ( myFSS, opening );    // call the userProc
  680.                         }
  681.                 
  682.                     OSErr err2 = theApp->PostFlightDocs ( opening, (short) itemsInList );    // cleanup time
  683.                     if ( err == noErr )
  684.                         err = err2;    // return the first error
  685.                     }
  686.                 }
  687.             }
  688.             
  689.     //    If we got the list successfully, dispose of it, saving any existing error
  690.         err2 = AEDisposeDesc ( &docList );
  691.         if ( err == noErr )
  692.             err = err2;
  693.         }
  694.     
  695. //    Reply only if the caller said to
  696.     if ( reply->dataHandle != NULL ) {
  697.         err2 = AEPutParamPtr ( reply, 'errn', 'shor', (Ptr) &err, sizeof ( err ));
  698.         if ( err == noErr )
  699.             err = err2;
  700.         }
  701.  
  702. //    Report an error here!
  703.     if ( err != noErr )    {
  704.         ErrorAlert ( kAlertID, kErrStringID, kAEVTErr, err );
  705.         }
  706.             
  707.     // ReportError ( err );
  708.     return err;
  709.     }
  710.  
  711.  
  712. /*
  713.     This routine is the handler for the odoc (Open Document) event.    
  714.     The odoc event simply calls the common _HandleDocs routines, which will
  715.     do the dirty work of parsing the AEVT & calling the userProcs.
  716. */
  717. #pragma segment Main
  718. pascal OSErr HandleODOC ( AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon ) {
  719.     OSErr    err = noErr;
  720.     
  721. //    call the low-level routine
  722.     err = _HandleDocs ( theAppleEvent, reply, handlerRefcon, true );
  723.     
  724. //    ReportError ( err );
  725.     return err;
  726.     }
  727.  
  728.  
  729. /*
  730.     This routine is the handler for the pdoc (Print Document) event.
  731.     The pdoc event like the odoc simply calls the common _HandleDocs routines
  732. */
  733. #pragma segment Main
  734. pascal OSErr HandlePDOC ( AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon ) {
  735.     OSErr    err = noErr;
  736.     
  737. //    call the low-level routine
  738.     err = _HandleDocs ( theAppleEvent, reply, handlerRefcon, false );
  739.     
  740. //    ReportError ( err );
  741.     return err;
  742.     }
  743.  
  744.  
  745. /*    
  746.     This is the routine called by the main event loop, when a high level
  747.     event is found.  Since we only deal with Apple events, and not other
  748.     high level events, we just pass everything onto the AEM via AEProcessAppleEvent
  749. */
  750. #pragma segment Main
  751. pascal void DoHighLevelEvent ( EventRecord *event ) {
  752. //    FailErr ( AEProcessAppleEvent ( event ));
  753.     AEProcessAppleEvent ( event );
  754.     }
  755.  
  756.  
  757. #pragma segment Main
  758. static void ErrorAlert ( short alertID, short stringListID, short stringIndexID, short errorID ) {
  759.     Str255    param, errorStr;
  760.  
  761. //    Set the cursor back to an arrow
  762.     SetCursor ( &qd.arrow );
  763.     
  764. //    Fill in the info.
  765.     NumToString ( errorID, errorStr );
  766.     GetIndString ( param, stringListID, stringIndexID );
  767.     ParamText ( param,  errorStr, NULL, NULL );
  768.  
  769. //    Show the alert
  770.     (void) Alert ( alertID, NULL );
  771.     }
  772.  
  773. #pragma mark ---User Procs---
  774.  
  775. /*
  776.     These routines let you do one-time setup and teardown.
  777.     They are called at app startup and shutdown.
  778. */
  779. #pragma segment Main
  780. OSErr CDropShell::InitGlobals  ( void ) { return noErr; }
  781.  
  782. #pragma segment Main
  783. void  CDropShell::DeInitGlobals    ( void ) {}
  784.  
  785.  
  786.  
  787. /*    
  788.     This routine is called when an OAPP event is received.
  789.     
  790.     Currently, all it does is set the gOApped flag, so you know that
  791.     you were called initally with no docs, and therefore you shouldn't 
  792.     quit when done processing any following odocs.
  793. */
  794. #pragma segment Main
  795. void CDropShell::OpenApp ( void ) {
  796.     fOApped = true;
  797.  
  798. //    let's show the user the splash screen
  799.     if ( fSplashScreen != nil )
  800.         ShowWindow ( fSplashScreen );
  801. }
  802.  
  803.  
  804. /*    
  805.     This routine is called when an QUIT event is received.
  806.     
  807.     We simply set the global done flag so that the main event loop can
  808.     gracefully exit.  We DO NOT call ExitToShell for two reasons:
  809.     1) It is a pretty ugly thing to do, but more importantly
  810.     2) The Apple event manager will get REAL upset!
  811. */
  812. #pragma segment Main
  813. void CDropShell::QuitApp (void) { fDone = true; }
  814.  
  815.  
  816. /*
  817.     This routine is called when the user chooses "Select File…" from the
  818.     File Menu.
  819.     
  820.     Currently it simply calls the new StandardGetFile routine to have the
  821.     user select a single file (any type, numTypes = -1) and then calls the
  822.     SendODOCToSelf routine in order to process it.  
  823.             
  824.     The reason we send an odoc to ourselves is two fold: 1) it keeps the code
  825.     cleaner as all file openings go through the same process, and 2) if events
  826.     are ever recordable, the right things happen (this is called Factoring!)
  827.  
  828.     Modification of this routine to only select certain types of files, selection
  829.     of multiple files, and/or handling of folder & disk selection is left 
  830.     as an exercise to the reader.
  831. */
  832. #pragma segment Main
  833. void CDropShell::SelectFile () {
  834.     StandardFileReply    stdReply;
  835.     SFTypeList            theTypeList;
  836.  
  837.     StandardGetFile ( NULL, -1, theTypeList, &stdReply );
  838.     if ( stdReply.sfGood )    // user did not cancel
  839.         SendODOCToSelf ( &stdReply.sfFile );    // so send me an event!
  840.     }
  841.  
  842.  
  843. #pragma mark --Document Handling--
  844.  
  845. /*    
  846.     This routine is the first one called when an ODOC or PDOC event is received.
  847.     
  848.     In this routine you would place code used to setup structures, etc. 
  849.     which would be used in a 'for all docs' situation (like "Archive all
  850.     dropped files")
  851.  
  852.     Obviously, the opening boolean tells you whether you should be opening
  853.     or printing these files based on the type of event recieved.
  854.     
  855.     NEW IN 2.0!
  856.     The itemCount parameter is simply the number of items that were dropped on
  857.     the application and that you will be processing.  This gives you the ability
  858.     to do a single preflight for memory allocation needs, rather than doing it
  859.     once for each item as in previous versions.
  860.     
  861.     We also return a boolean to tell the caller if you support this type
  862.     of event.  By default, our dropboxes don't support the pdoc, so when
  863.     opening is FALSE, we return FALSE to let the caller send back the
  864.     proper error code to the AEManager.
  865.  
  866. */
  867. #pragma segment Main
  868. Boolean CDropShell::PreFlightDocs (Boolean opening, short /*itemCount*/) {
  869.     return opening;        // we support opening, but not printing - see above
  870.     }
  871.  
  872.  
  873. /*    
  874.     This routine is called for each file passed in the ODOC event.
  875.     
  876.     In this routine you would place code for processing each file/folder/disk that
  877.     was dropped on top of you.
  878. */
  879. #pragma segment Main
  880. /*    Is the “file” represented by this FSSpec really a folder? */
  881. static Boolean    FSpIsFolder ( FSSpecPtr theFSSpec ) {
  882.     OSErr        err = noErr;
  883.     CInfoPBRec    pb;
  884.     Str255        fName;
  885.     
  886.     if ( theFSSpec->parID == fsRtParID )    // it's a volume!!
  887.         return true;
  888.         
  889.     BlockMoveData (theFSSpec->name, fName, 32);
  890.     pb.hFileInfo.ioDirID        = theFSSpec->parID;
  891.     pb.hFileInfo.ioCompletion    = NULL;
  892.     pb.hFileInfo.ioNamePtr        = fName;
  893.     pb.hFileInfo.ioVRefNum        = theFSSpec->vRefNum;
  894.     pb.hFileInfo.ioFDirIndex    = 0;
  895.     pb.hFileInfo.ioFVersNum        = 0;
  896.     err = PBGetCatInfoSync ( &pb );
  897.     
  898.     if ( err == noErr && ( pb.hFileInfo.ioFlAttrib & ioDirMask ) != 0 )
  899.         return true;
  900.     
  901.     return false;
  902.     }
  903.  
  904. OSErr CDropShell::OpenDoc ( FSSpec &myFSSpec, Boolean opening ) {
  905.     OSErr    err = noErr;
  906.     
  907.     if ( opening )
  908.         if ( fWalkFolders ) {
  909.         /*    For this case we need to determine if the FSSpec is a file or folder. */
  910.         /*    If it's a folder, we then need to process each item in that folder, */
  911.         /*    otherwise just process the item. */
  912.             if ( FSpIsFolder ( &myFSSpec ))
  913.                 err = ProcessFolder ( myFSSpec );
  914.             else
  915.                 err = ProcessItem ( myFSSpec );
  916.             }
  917.         else {
  918.         /*    For this case we just call ProcessItem on the FSSpec above.    */
  919.             err = ProcessItem ( myFSSpec );
  920.         }
  921.         
  922.     // ReportError ( err );
  923.     return err;
  924.     }
  925.  
  926.  
  927. /*    
  928.     This routine is the last routine called as part of an ODOC event.
  929.     
  930.     In this routine you would place code to process any structures, etc. 
  931.     that you setup in the PreflightDocs routine.
  932.  
  933.     NEW IN 2.0!
  934.     The itemCount parameter was the number of items that you processed.
  935.     It is passed here just in case you need it ;)  
  936.     
  937. */
  938. #pragma segment Main
  939. OSErr CDropShell::PostFlightDocs ( Boolean opening, short /*itemCount*/ ) {
  940.     OSErr err = noErr;
  941.     
  942.     if ( opening && !fOApped )
  943.         fDone = true;    //    close everything up!
  944.  
  945.     /*
  946.         The reason we do not auto quit is based on a recommendation in the
  947.         Apple event Registry which specifically states that you should NOT
  948.         quit on a 'pdoc' as the Finder will send you a 'quit' when it is 
  949.         ready for you to do so.
  950.     */
  951.     // ReportError ( err );
  952.     return err;
  953.     }
  954.  
  955.  
  956. /*
  957.     This routine gets called for each item (which could be either a file or a folder)
  958.     that the caller wants dropped.  The determining factor is the definition of the 
  959.     qWalkFolder compiler directive.   Either way, the item in question should be
  960.     processed as a single item and not "dissected" into component units (like subfiles
  961.     of a folder!)
  962. */
  963. OSErr CDropShell::ProcessItem ( FSSpec & /* theFile */ ) {
  964.     OSErr    err = noErr;
  965.     
  966. //    do something here
  967.     
  968.     // ReportError ( err );
  969.     return err;
  970.     }
  971.  
  972.  
  973. /*
  974.     This routine gets called for any folder (or disk) that the caller wants 
  975.     processed as a set of component items, instead of as a single entity.
  976.     The determining factor is the definition of the qWalkFolder compiler directive.
  977. */
  978. OSErr CDropShell::ProcessFolder ( FSSpec &theFile ) {
  979.     OSErr        err = noErr;
  980.     TDirectoryIterator    iter ( &theFile );
  981.     FSSpec        aSpec;
  982.     Boolean        isFolder;
  983.     
  984.     iter.Start ();
  985.     while ( err == noErr && iter.More ()) {
  986.         err = iter.Next ( &aSpec, &isFolder );
  987.         if ( err == noErr ) {
  988.             if ( isFolder )
  989.                 err = ProcessFolder ( aSpec );
  990.              else
  991.                 err = ProcessItem ( aSpec );
  992.             }        
  993.         }
  994.     
  995.     // ReportError ( err );
  996.     return err;
  997.     }
  998.  
  999. #pragma segment CompilerCruft
  1000.